const debounce = (delay, callback) => {
    let lastTime

    return function () {
        clearTimeout(lastTime)

        const [that, args] = [this, arguments]

        lastTime = setTimeout(() => {
            callback.apply(that, args)
        }, delay)
    }
}

const observerDomResize = (dom, callback) => {
    const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver

    const observer = new MutationObserver(callback)

    observer.observe(dom, {attributes: true, attributeFilter: ['style'], attributeOldValue: true})

    return observer
}

const deepMerge = (target, merged) => {
    for (var key in merged) {
        if (target[key] && typeof target[key] === 'object') {
            deepMerge(target[key], merged[key])

            continue
        }

        if (typeof merged[key] === 'object') {
            target[key] = deepClone(merged[key], true)

            continue
        }

        target[key] = merged[key]
    }

    return target
}

const deepClone = (object, recursion = false) => {
    if (!object) return object

    const {parse, stringify} = JSON

    if (!recursion) return parse(stringify(object))

    const clonedObj = object instanceof Array ? [] : {}

    if (object && typeof object === 'object') {
        for (let key in object) {
            if (object.hasOwn(key)) {
                if (object[key] && typeof object[key] === 'object') {
                    clonedObj[key] = deepClone(object[key], true)
                } else {
                    clonedObj[key] = object[key]
                }
            }
        }
    }

    return clonedObj
}


/* eslint-disable prefer-rest-params */
const merge = (...rest) => {
    const obj = {};
    const il = rest.length;
    let key;
    let i = 0;
    for (; i < il; i += 1) {
        // eslint-disable-next-line no-restricted-syntax
        for (key in rest[i]) {
            if (rest[i].hasOwnProperty(key)) {
                if (
                    typeof obj[key] === 'object' &&
                    typeof rest[i][key] === 'object' &&
                    obj[key] !== undefined &&
                    obj[key] !== null &&
                    !Array.isArray(obj[key]) &&
                    !Array.isArray(rest[i][key])
                ) {
                    obj[key] = {
                        ...obj[key],
                        ...rest[i][key],
                    };
                } else {
                    obj[key] = rest[i][key];
                }
            }
        }
    }
    return obj
};

const isNil = (value) => value === null || value === undefined;

const isEmpty = (value) => isNil(value) || (typeof value === 'string' && value.trim() === '');

const isObject = (o) => {
    return Object.prototype.toString.call(o) === '[object Object]';
}

const isPlainObject = (o) => {
    if (isObject(o) === false) return false;

    // If has modified constructor
    const ctor = o.constructor;
    if (ctor === undefined) return true;

    // If has modified prototype
    const prot = ctor.prototype;
    if (isObject(prot) === false) return false;

    // If constructor does not have an Object-specific method
    if (prot.hasOwnProperty('isPrototypeOf') === false) {
        return false;
    }

    // Most likely a plain Object
    return true;
}

const transformSubmitValue = (values, omit) => {
    if (typeof values !== 'object' || isNil(values) || values instanceof Blob) {
        return values;
    }
    let finalValues = {};

    Object.keys(values).forEach((entityKey) => {
        const itemValue = values[entityKey];
        if(omit){
            if(!isEmpty(itemValue)){
                finalValues[entityKey] = itemValue
            }
        }else{
            finalValues[entityKey] = itemValue
        }
    })

    return finalValues;
}

const saveBlobToFile = (blobData, fileName) =>{
    if(!blobData) return
    const blob = new Blob([blobData])
    if ('download' in document.createElement('a')) {
        const elink = document.createElement('a')
        elink.download = fileName
        elink.style.display = 'none'
        elink.href = URL.createObjectURL(blob)
        document.body.appendChild(elink)
        elink.click()
        URL.revokeObjectURL(elink.href)
        document.body.removeChild(elink)
    } else {
        navigator.msSaveBlob(blob, fileName)
    }
}

const mergeOptions = (defaults, config) => {
    const options = {};
    let key;
    for (key in defaults) {
        options[key] = defaults[key];
    }
    for (key in config) {
        if (config.hasOwnProperty(key)) {
            const value = config[key];
            if (typeof value !== 'undefined') {
                options[key] = value;
            }
        }
    }
    return options;
}

export {
    debounce,
    observerDomResize,
    deepMerge,
    deepClone,
    merge,
    isNil,
    isEmpty,
    isObject,
    transformSubmitValue,
    saveBlobToFile,
    mergeOptions
};